home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 3006 / 3006.xpi / components / dhYoutubeLinksContextItem.js < prev    next >
Text File  |  2010-01-15  |  13KB  |  432 lines

  1. /******************************************************************************
  2.  *            Copyright (c) 2006-2009 Michel Gutierrez. All Rights Reserved.
  3.  ******************************************************************************/
  4.  
  5. /**
  6.  * Constants.
  7.  */
  8.  
  9. const NS_YTLCITEM_CID = Components.ID("{fc78d3e8-91f0-4ccd-a830-aefd00595f43}");
  10. const NS_YTLCITEM_PROG_ID = "@downloadhelper.net/youtube-links-context-item;1";
  11. const DHNS = "http://downloadhelper.net/1.0#";
  12.  
  13. var Util=null;
  14. var Node=null;
  15.  
  16. /**
  17. * Object constructor
  18. */
  19. function YTLCItem() {
  20.     try {
  21.         //dump("[YTLCItem] constructor\n");
  22.         this.core=Components.classes["@downloadhelper.net/core;1"].
  23.             getService(Components.interfaces.dhICore);
  24.         var prefService=Components.classes["@mozilla.org/preferences-service;1"]
  25.                                            .getService(Components.interfaces.nsIPrefService);
  26.         this.pref=prefService.getBranch("dwhelper.");
  27.         this.currentPage=null;
  28.         this.pageQueue=[];
  29.     } catch(e) {
  30.         dump("[YTLCItem] !!! constructor: "+e+"\n");
  31.     }
  32. }
  33.  
  34. YTLCItem.prototype = {}
  35.  
  36. YTLCItem.prototype.canHandle=function(document,window,item) {
  37.     //dump("[YTLCItem] canHandle()\n");
  38.     try {
  39.         var popupNode=document.popupNode;
  40.         if(popupNode==null)
  41.             popupNode=this.getPopupNode(window);
  42.         if(popupNode==null)
  43.             return false;
  44.         var links=this.getYTLinks(popupNode);
  45.         /*
  46.         dump("=>Found "+links.length+" links\n");
  47.         for(var i in links) {
  48.             dump("  - "+links[i]+"\n");
  49.         }
  50.         */
  51.         if(links.length>0)
  52.             return true;
  53.         return false;
  54.     } catch(e) {
  55.         dump("!!! [YTLCItem] canHandle(): "+e+"\n");
  56.     }
  57. }
  58.  
  59. YTLCItem.prototype.handle=function(document,window,item) {
  60.     //dump("[YTLCItem] handle()\n");
  61.     try {
  62.         var popupNode=document.popupNode;
  63.         if(popupNode==null)
  64.             popupNode=this.getPopupNode(window);
  65.         if(popupNode==null)
  66.             return false;
  67.         var links=this.getYTLinks(popupNode);
  68.         for(var i=0;i<links.length;i++) {
  69.             this.queuePage(links[i]);
  70.         }
  71.     } catch(e) {
  72.         dump("!!! [YTLCItem] handle(): "+e+"\n");
  73.     }
  74. }
  75.  
  76. YTLCItem.prototype.getYTLinks=function(popupNode) {
  77.     var doc=popupNode.ownerDocument;
  78.     var baseUri=Components.classes["@mozilla.org/network/io-service;1"]
  79.                .getService(Components.interfaces.nsIIOService)
  80.                .newURI(doc.URL, null, null);
  81.     var links=[];
  82.     var seln=doc.defaultView.getSelection();
  83.     if(seln.rangeCount>0) {
  84.         var range=seln.getRangeAt(0);
  85.         if(!range.collapsed) {
  86.             var linksMap={};
  87.             var firstLink=this.getLinkInAncestors(range.startContainer,baseUri);
  88.             if(firstLink!=null)
  89.                 linksMap[firstLink]="";
  90.             var ancestor=range.commonAncestorContainer;
  91.             var url0=this.getLinkInAncestors(ancestor, baseUri);
  92.             if(url0!=null) {
  93.                 links.push(url0);
  94.             } else {
  95.                 this.getLinks(ancestor, linksMap, baseUri, { 
  96.                     inRange: false, 
  97.                     startContainer: range.startContainer,
  98.                     endContainer: range.endContainer
  99.                     });
  100.                 for(var url in linksMap) {
  101.                     links.push(url);
  102.                 }
  103.             }
  104.             return links;
  105.         }
  106.     } 
  107.     
  108.     if(popupNode) {
  109.         var url=this.getLinkInAncestors(popupNode, baseUri);
  110.         if(url!=null)
  111.             links.push(url);
  112.     }
  113.     
  114.     return links;
  115. }
  116.  
  117. YTLCItem.prototype.getLinks=function(node,linksMap, baseUri, scanData) {
  118.     if(scanData.inRange==false && node==scanData.startContainer)
  119.         scanData.inRange=true;
  120.  
  121.     if(node.nodeType==Node.ELEMENT_NODE) {
  122.         if(scanData.inRange==true) {
  123.             if(node.tagName.toLowerCase()=="a") {
  124.                 var url=this.getURL(baseUri,node);
  125.                 if(url)
  126.                     linksMap[url]="";
  127.             }
  128.         }
  129.         var node0=node.firstChild;
  130.         while(node0) {
  131.             this.getLinks(node0,linksMap,baseUri,scanData);
  132.             node0=node0.nextSibling;
  133.         }
  134.     }
  135.  
  136.     if(scanData.inRange==true && node==scanData.endContainer)
  137.         scanData.inRange=false;
  138. }
  139.  
  140. YTLCItem.prototype.getLinkInAncestors=function(node,baseUri) {
  141.     while(node!=null) {
  142.         if(node.nodeType==Node.ELEMENT_NODE) {
  143.             if(node.tagName.toLowerCase()=="a") {
  144.                 var url=this.getURL(baseUri,node);
  145.                 if(url)
  146.                     return url;
  147.             }
  148.         }
  149.         node=node.parentNode;
  150.     }
  151.     return null;
  152. }
  153.  
  154. YTLCItem.prototype.getURL=function(baseUri,node) {
  155.     var href=node.getAttribute("href");
  156.     if(href!=null && href.length>0) {
  157.         var url=baseUri.resolve(href);
  158.         if(url.match(/^http\:\/\/(?:[^\/\.]+\.)?youtube\.com\/watch\?/))
  159.             return url;
  160.     }    
  161.     if(node.hasAttribute("onclick")) {
  162.         try {
  163.             var videoId=/onPlayVideos\(\['([a-zA-Z0-9]*)'\]\)/.exec(node.getAttribute("onclick"))[1];
  164.             if(videoId) {
  165.                 return "http://www.youtube.com/watch?v="+videoId;
  166.             }
  167.         } catch(e) {}
  168.     }
  169.     return null;
  170. }
  171.  
  172. YTLCItem.prototype.getPopupNode=function(window) {
  173.     //dump("[YTLCItem] getPopupNode("+window.document.URL+")\n");
  174.     var popupNode=window.document.popupNode;
  175.     return popupNode;
  176. }
  177.  
  178. YTLCItem.prototype.queuePage=function(url) {
  179.     this.pageQueue.push(url);
  180.     this.checkQueue();
  181. }
  182.  
  183. YTLCItem.prototype.checkQueue=function() {
  184.     if(this.currentPage!=null)
  185.         return;
  186.     if(this.pageQueue.length>0) {
  187.         this.currentPage=this.pageQueue.shift();
  188.         this.getYTPage(this.currentPage);
  189.     }
  190. }
  191.  
  192. YTLCItem.prototype.getYTPage=function(url) {
  193.     
  194.     function StreamListener(service,url) {
  195.         this.service=service;
  196.         this.url=url;
  197.     }
  198.  
  199.     StreamListener.prototype={
  200.             QueryInterface: function(iid) {
  201.                 if (!iid.equals(Components.interfaces.nsISupports) && 
  202.                     !iid.equals(Components.interfaces.nsIStreamListener)) {
  203.                         throw Components.results.NS_ERROR_NO_INTERFACE;
  204.                     }
  205.                 return this;
  206.             },
  207.             onStartRequest: function(request,context) {
  208.                 this.httpChannel=request.QueryInterface(Components.interfaces.nsIHttpChannel);
  209.                 this.responseStatus=this.httpChannel.responseStatus;
  210.                 this.data="";
  211.             },
  212.             onDataAvailable: function(request,context,inputStream,offset,count) {
  213.                 var sstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
  214.                        .createInstance(Components.interfaces.nsIConverterInputStream);
  215.                 sstream.init(inputStream, "utf-8", 256, 
  216.                     Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  217.                 var str={};
  218.                 var n=sstream.readString(128,str);
  219.                 while(n>0) {
  220.                     this.data+=str.value;
  221.                     str={};
  222.                     n=sstream.readString(128,str);
  223.                 }
  224.             },
  225.             onStopRequest: function(request,context,nsresult) {
  226.                 if(this.responseStatus==200) {
  227.                     this.service.loadedPage(this.url,this.data);
  228.                 }
  229.                 this.service.currentPage=null;
  230.                 this.service.checkQueue();
  231.             }
  232.         }
  233.  
  234.     var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  235.                                        .getService(Components.interfaces.nsIIOService);
  236.      var uri = ioService.newURI(url, null, null);
  237.      var channel = ioService.newChannelFromURI(uri);
  238.      channel.asyncOpen(new StreamListener(this,url), null);
  239. }
  240.  
  241. YTLCItem.prototype.loadedPage=function(url,text) {
  242.     //dump("[YTLCItem] loadedPage("+url+","+text.length+" bytes)\n");
  243.     try {
  244.         var title=null;
  245.         var videoId=null;
  246.         var tParam=null;
  247.         var pageUrl=url;
  248.         var m=/meta name="title" content="(.*)"/.exec(text);
  249.         if(m!=null && m.length==2)
  250.             title=m[1];
  251.         m=/\"video_id\": \"(.*?)\".*\"t(?:oken)?\": \"(.*?)\"/.exec(text);
  252.         if(m!=null && m.length==3) {
  253.             videoId=m[1];
  254.             tParam=m[2];
  255.         } else {
  256.             m=/\"t(?:oken)?\": \"(.*?)\".*\"video_id\": \"(.*?)\"/.exec(text);
  257.             if(m!=null && m.length==3) {
  258.                 videoId=m[2];
  259.                 tParam=m[1];
  260.             }
  261.         }
  262.         
  263.         if(title!=null && videoId!=null & tParam!=null) {
  264.             var url="http://www.youtube.com/get_video?video_id=";
  265.             url+=videoId;
  266.             url+="&t=";
  267.             url+=tParam;
  268.             var orgFileName=title+".flv";
  269.             orgFileName=orgFileName.replace(/[^a-zA-Z0-9\.\-]/g,"_");
  270.             var format=null;
  271.             var fileName=orgFileName;
  272.             
  273.             var desc=Components.classes["@mozilla.org/properties;1"].
  274.                 createInstance(Components.interfaces.nsIProperties);
  275.             Util.setPropsString(desc,"media-url",url);
  276.             Util.setPropsString(desc,"page-url",pageUrl);
  277.             Util.setPropsString(desc,"label",title);
  278.             
  279.             var fileName=title;
  280.             var unmodifiedFilename=false;
  281.             try {
  282.                 unmodifiedFilename=this.pref.getBoolPref("yt-unmodified-filename");        
  283.             } catch(e) {}
  284.             fileName=fileName.replace(/[\/"\?\*:\|"'\\]/g,"_");
  285.             if(unmodifiedFilename==false) {
  286.                 var keepSpaces=false;
  287.                 try {
  288.                     keepSpaces=this.pref.getBoolPref("yt-keep-spaces");
  289.                 } catch(e) {}
  290.                 if(keepSpaces)
  291.                     fileName=fileName.replace(/[^a-zA-Z0-9\.\- ]/g,"_");
  292.                 else
  293.                     fileName=fileName.replace(/[^a-zA-Z0-9\.\-]/g,"_");
  294.             }
  295.             Util.setPropsString(desc,"file-name",fileName);
  296.             Util.setPropsString(desc,"icon-url","http://www.youtube.com/favicon.ico");
  297.  
  298.             var doPreferHQ=true;
  299.             try {
  300.                 doPreferHQ=this.pref.getBoolPref("yt-prefer-hq");
  301.             } catch(e) {}
  302.             if(doPreferHQ) {
  303.                 var checker=Components.classes["@downloadhelper.net/ythq-checker;1"].
  304.                     createInstance(Components.interfaces.dhIYTHQChecker);
  305.                 checker.check(url,this,desc);
  306.  
  307.             } else {
  308.                 Util.setPropsString(desc,"file-name",fileName+".flv");
  309.                 this.core.quickProcess(desc);
  310.             }
  311.         }
  312.     } catch(e) {
  313.         dump("!!! [YTLCItem] loadedPage("+url+","+text.length+" bytes)\n");
  314.     }
  315. }
  316.  
  317. YTLCItem.prototype.checkedYTHQ=function(url,desc,format,extension) {
  318.     //dump("[YTLCItem] checkedYTHQ("+url+",args)\n");
  319.     var fileName=Util.getPropsString(desc,"file-name");
  320.     if(url!=null) {
  321.         Util.setPropsString(desc,"file-name",fileName+"."+extension);
  322.         Util.setPropsString(desc,"media-url",url);
  323.     } else {
  324.         Util.setPropsString(desc,"file-name",fileName+".flv");
  325.     }
  326.     this.core.quickProcess(desc);
  327. }
  328.  
  329. YTLCItem.prototype.QueryInterface = function(iid) {
  330.     //dump("[YTLCItem] QueryInterface("+iid+")\n");
  331.     if(
  332.         iid.equals(Components.interfaces.dhIContextItem) ||
  333.         iid.equals(Components.interfaces.dhIYTHQCheckerListener) ||
  334.         iid.equals(Components.interfaces.nsISupports)
  335.     ) {
  336.         return this;
  337.     }
  338.     throw Components.results.NS_ERROR_NO_INTERFACE;
  339. }
  340.  
  341. var vYTLCItemModule = {
  342.     firstTime: true,
  343.     
  344.     /*
  345.      * RegisterSelf is called at registration time (component installation
  346.      * or the only-until-release startup autoregistration) and is responsible
  347.      * for notifying the component manager of all components implemented in
  348.      * this module.  The fileSpec, location and type parameters are mostly
  349.      * opaque, and should be passed on to the registerComponent call
  350.      * unmolested.
  351.      */
  352.     registerSelf: function (compMgr, fileSpec, location, type) {
  353.  
  354.         if (this.firstTime) {
  355.             this.firstTime = false;
  356.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  357.         }
  358.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  359.         compMgr.registerFactoryLocation(NS_YTLCITEM_CID,
  360.                                         "YTLCItem",
  361.                                         NS_YTLCITEM_PROG_ID, 
  362.                                         fileSpec,
  363.                                         location,
  364.                                         type);
  365.     },
  366.  
  367.     unregisterSelf: function(compMgr, fileSpec, location) {
  368.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  369.         compMgr.unregisterFactoryLocation(NS_DH_YTLCITEM_CID, fileSpec);
  370.     },
  371.  
  372.     /*
  373.      * The GetClassObject method is responsible for producing Factory and
  374.      * SingletonFactory objects (the latter are specialized for services).
  375.      */
  376.     getClassObject: function (compMgr, cid, iid) {
  377.         if (!cid.equals(NS_YTLCITEM_CID)) {
  378.             throw Components.results.NS_ERROR_NO_INTERFACE;
  379.         }
  380.  
  381.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  382.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  383.         }
  384.  
  385.         return this.vYTLCItemFactory;
  386.     },
  387.  
  388.     /* factory object */
  389.     vYTLCItemFactory: {
  390.         /*
  391.          * Construct an instance of the interface specified by iid, possibly
  392.          * aggregating it with the provided outer.  (If you don't know what
  393.          * aggregation is all about, you don't need to.  It reduces even the
  394.          * mightiest of XPCOM warriors to snivelling cowards.)
  395.          */
  396.         createInstance: function (outer, iid) {
  397.             if (outer != null) {
  398.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  399.             }
  400.     
  401.             if(Node==null)
  402.                 Node=Components.interfaces.nsIDOMNode;
  403.  
  404.             if(Util==null) 
  405.                 Util=Components.classes["@downloadhelper.net/util-service;1"]
  406.                     .getService(Components.interfaces.dhIUtilService);
  407.  
  408.             return new YTLCItem().QueryInterface(iid);
  409.         }
  410.     },
  411.  
  412.     /*
  413.      * The canUnload method signals that the component is about to be unloaded.
  414.      * C++ components can return false to indicate that they don't wish to be
  415.      * unloaded, but the return value from JS components' canUnload is ignored:
  416.      * mark-and-sweep will keep everything around until it's no longer in use,
  417.      * making unconditional ``unload'' safe.
  418.      *
  419.      * You still need to provide a (likely useless) canUnload method, though:
  420.      * it's part of the nsIModule interface contract, and the JS loader _will_
  421.      * call it.
  422.      */
  423.     canUnload: function(compMgr) {
  424.         return true;
  425.     }
  426. };
  427.  
  428. function NSGetModule(compMgr, fileSpec) {
  429.     return vYTLCItemModule;
  430. }
  431.  
  432.